<template>
  <div class="g6-process-container" :class="{'reset-padding':!processCanPublish }" >
    <!--    左侧按钮-->
    <panel v-if="processCanPublish" :elements="elements" @canvas-add-node="addNode"/>
    <graph ref="graph" :data="data" />
    <!--配置面板-->

    <div id="configPanel" :class="{ hidden: !configVisible }">
      <i class="gb-toggle-btn" @click="handleConfigVisible('node')"></i>
      <div class="panel-title">
        状态配置
      </div>
      <el-form :disabled="!processCanPublish" class="pl10" ref="nodeForm" :model="nodeForm" label-position="top">
        <el-form-item
          label="状态编号"
          prop="code"
          :rules="[
            { required: true, message: '状态编号不能为空', trigger: 'change' },
            { max: 28, message: '最大长度为28', trigger: 'change' },
            { pattern: COMMON_CODE_28I_REG.pattern, message: '请输入有效的状态编号', trigger: 'change' },
            { validator: codeValidator, trigger: 'change' },
          ]"
        >
          <template slot="label">状态编号
            <el-tooltip popper-class="custom-popper" :content="COMMON_CODE_28I_REG.message" placement="top">
              <i class="el-icon-question" />
            </el-tooltip>
          </template>
          <el-input v-model="nodeForm.code" maxlength="28" show-word-limit @input="changeNodeCode"/>
        </el-form-item>
        <el-form-item
          label="状态名称"
          prop="label"
          :rules="[
            { required: true, message: '状态名称不能为空', trigger: 'change' },
            { validator: labelValidator, trigger: 'change' },
          ]"
        >
          <el-input v-model="nodeForm.label" @input="changeNodeLabel"/>
        </el-form-item>
        <el-form-item
          label="是否默认状态"
          prop="isDefault"
        >
          <el-switch v-model="nodeForm.isDefault" @input="changeNodeDefault"/>
        </el-form-item>
      </el-form>
    </div>
    <!-- 线的配置面板 -->
    <div id="configEdgePanel" :class="{ hidden: !configEdgeVisible }">
      <i class="gb-toggle-btn" @click="handleConfigVisible('edge')"></i>
      <div class="panel-title">
        事件配置
      </div>
      <el-form
        class="pl10"
        :disabled="!processCanPublish"
        ref="edgeForm"
        :model="edgeForm"
        label-position="top">
        <el-form-item
          label="事件编号"
          prop="code"
          :rules="[
            { required: true, message: '事件编号不能为空', trigger: 'change' },
            { pattern: COMMON_CODE_28I_REG.pattern, message: '请输入有效的事件编号', trigger: 'change' },
          ]"
        >
          <template slot="label">事件编号
            <el-tooltip popper-class="custom-popper" :content="COMMON_CODE_28I_REG.message" placement="top">
              <i class="el-icon-question" />
            </el-tooltip>
          </template>
          <el-input v-model="edgeForm.code" :maxlength="28" show-word-limit @input="changeEdgeItem"/>
        </el-form-item>
        <el-form-item
          label="事件名称"
          prop="label"
          :rules="[
            { required: true, message: '事件名称不能为空', trigger: 'change' },
          ]"
        >
          <el-input v-model="edgeForm.label" @input="changeEdgeItem"/>
        </el-form-item>
        <el-form-item
          label="活动脚本"
          prop="scriptId"
        >
          <tree-select
            v-model="edgeForm.scriptId"
            filterable
            :fieldMap="{
              value: 'id',
              label:'name',
              disabled: 'disabled'
            }"
            :filterNodeMethod="filterNodeMethod"
            :treeData="scriptList"
            @change="changeEdgeItem"
          />
        </el-form-item>
      </el-form>

    </div>

    <!--    图标小工具-->
    <tool-bar @edit="handleEdit" :process-can-publish="processCanPublish" @save="handleSave" />
    <!--    菜单右键-->
    <context-menu @edit="handleEdit" />
  </div>
</template>

<script>
import G6 from '@antv/g6'
import { fittingString } from './utils/fittingString'
import Panel from './widgets/panel/panel'
import Graph from './widgets/graph/graph'
import ToolBar from './widgets/toolbar/toolbar'
import ContextMenu from './widgets/contextmenu/contextmenu'

import registerBehavior from './behavior'
import registerShape from './shape'
import store from './store'
import { getHmscriptfolderListTree } from '@/apis/paas/HmScriptFolderController.js'
import { COMMON_CODE_28I_REG } from '@/utils/reg'
import { createScriptTreeNode, filterNodeMethod } from '@/utils/treeSelectFormat'
export default {
  name: 'G6Process',
  components: {
    Panel,
    Graph,
    ToolBar,
    ContextMenu
  },

  props: {
    elements: {
      type: Array,
      default: () => []
    },

    processCanPublish: {
      type: Boolean,
      default: true
    },
    data: {
      type: String,
      default: ''
    }
  },
  watch: {
    processCanPublish: function (val) {
      if (!val) {
        this.graph.processCanPublish = this.processCanPublish
      }
    }
  },
  data () {
    return {
      graph: null,
      filterNodeMethod,
      COMMON_CODE_28I_REG,
      nodeShapes: [
      ],
      nodeForm: {
        label: '',
        code: ''
      },
      edgeForm: {
        code: '',
        label: '',
        scriptId: ''
      },
      scriptList: [],
      scriptLoading: false,
      labelCfg: {
        fontSize: 12,
        fill: '#fff'
      },
      // 记录当前选中的边和节点
      currentNode: '',
      currentEdge: '',
      model: {}, // 记录node
      // 配置
      node: {
        code: '',
        name: '',
        isDefault: false
      },
      edge: {
        label: '',
        type: ''
      },
      configVisible: false, // 配置面板是否可见
      configEdgeVisible: false, // 边
      config: '',
      canvasOffset: {
        x: 0,
        y: 0
      }
    }
  },

  mounted () {
    registerBehavior(G6)
    registerShape(G6)
    this.initGraph()
    this.getHmscriptList()
  },

  methods: {
    initGraph () {
      this.graph = this.$refs.graph.drawGraph(G6)
      if (!this.processCanPublish) {
        this.graph.processCanPublish = this.processCanPublish
      }

      // 点击画布隐藏弹窗
      this.graph.on('after-canvas-click', e => {
        this.configEdgeVisible = false
        this.configVisible = false
        // 隐藏删除的右键菜单
        const contextMenu = document.querySelector('.g6-contextMenu')
        contextMenu.style.left = '-150px'
      })
      // 点击节点触发弹窗
      this.graph.on('after-node-selected', e => {
        // 节点点击后事件
        this.currentNode = e.item
        this.configVisible = !!e
        this.configEdgeVisible = !e
        if (e && e.item) {
          const model = e.item.get('model')
          this.model = JSON.parse(JSON.stringify(model))
          this.config = model
          this.nodeForm = {
            code: this.model.code,
            label: this.model._label,
            isDefault: this.model.isDefault
          }
          this.$nextTick(() => {
            this.$refs.nodeForm.clearValidate()
          })
          this.labelCfg = {
            fill: model.labelCfg.fill,
            fontSize: model.labelCfg.fontSize
          }
          this.node = {
            fill: model.style.fill,
            borderColor: model.style.stroke,
            lineDash: model.style.lineDash || 'none',
            width: model.style.width,
            height: model.style.height,
            shape: model.type
          }
        }
      })

      this.graph.on('after-edge-selected', e => {
        this.configEdgeVisible = !!e
        this.configVisible = !e
        // this.handleConfigVisible('edge')
        if (e && e.item) {
          this.currentEdge = e.item
          const model = e.item.get('model')
          this.model = JSON.parse(JSON.stringify(model))
          this.config = model
          this.edgeForm = {
            label: model.label,
            code: model.code,
            scriptId: model.scriptId
          }
          this.$nextTick(() => {
            this.$refs.edgeForm.clearValidate()
          })
          this.edge = {
            label: model.label,
            type: model.type,
            code: model.code,
            scriptId: model.scriptId
          }
          this.graph.updateItem(e.item, {
            // shape: 'line-edge',
            style: {
              radius: 10,
              lineWidth: 2
            }
          })
        }
      })

      this.graph.on('keydown', e=>{
        if(e.keyCode === 46){
          // 监听键盘delete删除节点
          if(!this.configEdgeVisible && !this.configVisible) return
          if(this?.model?.id){
            const currentItem = this.graph.findById(this.model.id)
            const type = currentItem.getType();
            if (type === "node") {
              const edges = currentItem.getEdges();
              edges.forEach(edge => {
                this.graph.removeItem(edge);
              });
            }
            this.graph.removeItem(currentItem);
            store.addStack()
          }
        }
      })
    },

    async handleNodeClick (data) {
      this.configVisible = true
      this.configEdgeVisible = false
      if (data) {
        const model = data
        this.model = JSON.parse(JSON.stringify(model))
        const item = this.graph.findById(model.id)
        this.currentEdge = this.graph.findById(model.id)
        const nodes = this.graph.findAllByState('node', 'selected')
        nodes.forEach(node => {
          if (node.getModel().id !== model.id) {
            this.graph.setItemState(node, 'selected', false)
          }
        })
        const edges = this.graph.findAllByState('edge', 'selected')
        edges.forEach(edge => {
          this.graph.setItemState(edge, 'selected', false)
        })
        this.graph.setItemState(item, 'selected', true)
        this.config = model
        this.nodeForm = {
          code: this.model.code,
          label: this.model._label,
          isDefault: this.model.isDefault
        }
        await this.$refs.nodeForm.validate()
      }
    },
    async handleEdgeClick (data) {
      this.configVisible = false
      this.configEdgeVisible = true
      if (data) {
        const model = data
        this.model = JSON.parse(JSON.stringify(model))
        this.config = model
        const item = this.graph.findById(model.id)
        this.currentEdge = this.graph.findById(model.id)
        const nodes = this.graph.findAllByState('node', 'selected')
        nodes.forEach(node => {
          this.graph.setItemState(node, 'selected', false)
        })
        const edges = this.graph.findAllByState('edge', 'selected')
        edges.forEach(edge => {
          if (edge.getModel().id !== model.id) {
            this.graph.setItemState(edge, 'selected', false)
          }
        })
        this.graph.setItemState(item, 'selected', true)
        this.edgeForm = {
          label: model.label,
          code: model.code,
          scriptId: model.scriptId
        }
        await this.$refs.edgeForm.validate()
      }
    },
    // 添加节点
    addNode (e) {
      const model = {
        text: 'node',
        // id:  Util.uniqueId(),
        // style: {
        //   radius: 5,
        //   stroke: '#ccc',
        //   fill: '#fff',
        //   lineWidth: 2
        // },
        // 坐标
        x: e.clientX - this.canvasOffset.x - 40,
        y: e.clientY - this.canvasOffset.y - 40
        // singleEdge: true//  todo 拖拽锚点只能生成 1 条边,需要进一步判断
      }
      // 形状
      if (e.target.dataset && e.target.dataset.shape) {
        // 形状
        model.type = e.target.dataset.shape
      }
      this.graph.addItem('node', model)
    },
    // getAnchorPoints () {
    //   return [
    //     [0, 0.5], // 左侧中间
    //     [0.5, 0],
    //     [1, 0.5], // 右侧中间
    //     [0.5, 1]
    //   ]
    // },
    handleEdit (data) {
      this.$emit('edit', data)
    },
    // 保存 graph的数据
    handleSave (data) {
      this.$emit('save', data)
    },

    initData (value) {
      // 处理获取的流程数据
      store.readData(value)
    },

    resetStack () {
      store.resetStack()
    },

    hasChange () {
      return store.hasChange()
    },
    handleConfigVisible (flag) {
      if (flag === 'edge') {
        this.configVisible = false
        this.configEdgeVisible = !this.configEdgeVisible
      } else {
        this.configEdgeVisible = false
        this.configVisible = !this.configVisible
      }
    },
    codeValidator (rule, value, callback) {
      const data = this.graph.save()
      if (data.nodes.find(node => node.code === (value || '').trim() && node.id !== this.model.id)) {
        callback(new Error('状态编号已存在'))
      } else {
        callback()
      }
    },
    labelValidator (rule, value, callback) {
      const data = this.graph.save()
      if (data.nodes.find(node => node._label === (value || '').trim() && node.id !== this.model.id)) {
        callback(new Error('状态名称已存在'))
      } else {
        callback()
      }
    },

    changeNodeCode (val) {
      // code唯一
      const data = this.graph.save()
      let flag = true
      data.nodes.map(node => {
        const { id, code } = node
        if (id !== this.model.id && code === this.nodeForm.code) {
          flag = false
        }
      })
      // if (!flag) return
      const item = this.graph.findById(this.model.id)
      this.graph.updateItem(item, {
        label: fittingString(this.nodeForm.label, 160, 16),
        _label: this.nodeForm.label,
        code: this.nodeForm.code,
        isDefault: this.nodeForm.isDefault
      })
    },
    changeNodeLabel (val) {
      const item = this.graph.findById(this.model.id)
      this.graph.updateItem(item, {
        label: fittingString(this.nodeForm.label, 160, 16),
        _label: this.nodeForm.label,
        code: this.nodeForm.code,
        isDefault: this.nodeForm.isDefault
      })
    },
    changeNodeDefault (val) {
      const item = this.graph.findById(this.model.id)
      item.isDefault = val
      if (val) {
        this.graph.updateItem(item, {
          label: fittingString(this.nodeForm.label, 160, 16),
          _label: this.nodeForm.label,
          code: this.nodeForm.code,
          isDefault: this.nodeForm.isDefault,
          style: {
            fill: '#ccc'
          }
        })
        // 默认节点唯一
        const data = this.graph.save()
        data.nodes.map(node => {
          const { id, isDefault } = node
          if (id !== this.model.id && isDefault) {
            const nodeGraph = this.graph.findById(id)
            this.graph.updateItem(nodeGraph, {
              isDefault: false,
              style: {
                fill: '#fff'
              }
            })
          }
        })
      } else {
        this.graph.updateItem(item, {
          label: fittingString(this.nodeForm.label, 160, 16),
          _label: this.nodeForm.label,
          code: this.nodeForm.code,
          isDefault: this.nodeForm.isDefault,
          style: {
            fill: '#fff'
          }
        })
      }
    },
    changeEdgeItem (val) {
      this.$nextTick(() => {
        this.graph.updateItem(this.currentEdge, {
          label: this.edgeForm.label,
          code: this.edgeForm.code,
          scriptId: this.edgeForm.scriptId
        })
      })
    },
    getHmscriptList () {
      this.scriptLoading = true
      getHmscriptfolderListTree().then(res => {
        res.forEach((node) => {
          node.type = 'folder'
        })
        this.scriptList = createScriptTreeNode(res || [])
      }).finally(() => {
        this.scriptLoading = false
      })
    }
  }
}
</script>

<style scoped lang="scss">
.g6-process-container {
  height: 100%;
  width: 100%;
  /* box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); */
  position: relative;
  overflow: hidden;
  padding-top: 48px;
  padding-left: 120px;
  box-sizing: border-box;
  border: 1px solid #eee;
  background: #fff;
}
.reset-padding {
  padding-left: 0px;
}

.g6-process-container ::-webkit-scrollbar {
  width: 5px;
  background-color: #eee;
}
.g6-process-container ::-webkit-scrollbar-thumb {
  border-radius: 0;
  background: #ccc;
}
.g6-process-container ::-webkit-scrollbar-track {
  border-radius: 0;
}

#configPanel,#configEdgePanel {
  position: absolute;
  top: 50px;
  right: 0;
  bottom: 0;
  z-index: 100;
  width: 25%;
  overflow: auto;
  background: #fff;
  //padding: 40px 0 10px;
  padding-right: 20px ;
  transition: transform 0.3s ease-in-out;
  box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
  border-left: 1px solid #dcdfe6;

&.hidden {
   transform: translate(100%, 0);
 }

.panel-title {
  font-weight: 700;
  margin:15px 10px 10px 10px;
}

.gb-toggle-btn {
  width: 10px;
  height: 20px;
  top: 50%;
  right: 100%;
  border-radius: 10px 0 0 10px;
  box-shadow: -2px 0 2px 0 rgba(0, 0, 0, 0.1);
  transform: translate(0, -50%);
}

.config-data {
  padding: 5px 10px;
}
.config-item {
input {
  width: 80px;
  padding: 5px;
  margin: 5px 10px;
  background: #f4f7fd;
  border-radius: 4px;
}
}
button {
  width: 80px;
  line-height: 24px;
  display: inline-block;
  padding: 5px 10px;
  border-radius: 6px;
  border: 1px solid #ccc;
  margin: 0 5px;
  cursor: pointer;
}
.save {
  color: #fff;
  background: #46a0fc;
  border-color: #46a0fc;
}
}
</style>
